#!/usr/bin/python
# -*- coding: utf8 -*-

import pyparsing as PP
from nodos_gramatica import *


#############################
# Definición de gramática   #
#############################

terminales = 'abcdefghijklmnopqrstuvwxyz'
no_terminales = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
simbolos = '+?*'


# Símbolos básicos
terminal = PP.Word(terminales, exact=1)
no_terminal = PP.Word(no_terminales, exact=1)
simbolo = PP.Word(simbolos, exact=1)

LPAR = PP.Literal('(').suppress()
RPAR = PP.Literal(')').suppress()
PIPE = PP.Literal('|')
END = PP.Literal('$').suppress()
DOSPUNTOS = PP.Literal(':').suppress()
PUNTOYCOMA = PP.Literal(';').suppress()

# Expresiones compuestas
expresion = PP.Forward()
simboloSolo = PP.Word(simbolos, exact=1)
atomo = LPAR + expresion + RPAR | terminal | no_terminal | simboloSolo
termino = atomo + PP.ZeroOrMore(simbolo)

restoConcatenacion = PP.Forward()
concatenacion = (termino + restoConcatenacion) | termino
restoConcatenacion << concatenacion

restoUnion = PP.Forward()
union = concatenacion + PIPE + restoUnion | \
        PIPE + restoUnion | \
        concatenacion + PIPE | \
        concatenacion | \
        PIPE
restoUnion << union

expresion << (union | PP.Empty())
linea_gram = no_terminal + DOSPUNTOS + expresion + PUNTOYCOMA
gramatica = PP.OneOrMore(PP.Group(linea_gram)) + PP.StringEnd()



#############################
# Callbacks para parse tree #
#############################

unarios = {'?': OperandoPreg,
           '*': OperandoEstrella,
           '+': OperandoMas
          }


def construirTermino(toks):
    base = toks[0]
    for i in range(1, len(toks)):
        base = unarios[toks[i]](base)
    return [base]

def construirUnion(toks):
    if len(toks) == 3:
        return [OperandoUnion(toks[0], toks[2])]
    elif len(toks) == 2:
        if toks[0] == '|':
            return [OperandoUnion(SimboloLambda(), toks[1])]
        elif toks[1] == '|':
            return [OperandoUnion(toks[0], SimboloLambda())]
        else:
            raise
    elif len(toks) == 1:
        if toks[0] == '|':
            return [OperandoUnion(SimboloLambda(), SimboloLambda())]
        else:
            return toks
    else:
        raise

def construirConcatenacion(toks):
    if len(toks) == 1:
        return toks
    else:
        base = toks[0]
        for i in range(1, len(toks)):
            base = OperandoConcat(base, toks[i])
        return [base]

def construirExpresion(toks):
    if len(toks) == 0:
        return [SimboloLambda()]
    else:
        return toks

def construirTerminal(toks):
    return [SimboloTerminal(toks[0])]

def construirNoTerminal(toks):
    return [SimboloNoTerminal(toks[0])]

def construirSimboloSolo(toks):
    return [unarios[toks[0]](SimboloLambda())]



#############################
# Bindings                  #
#############################

terminal.setParseAction(construirTerminal)
no_terminal.setParseAction(construirNoTerminal)
simboloSolo.setParseAction(construirSimboloSolo)
termino.setParseAction(construirTermino)
union.setParseAction(construirUnion)
concatenacion.setParseAction(construirConcatenacion)
expresion.setParseAction(construirExpresion)


#############################
# Tests                     #
#############################

if __name__ == '__main__':
    print "Ejecutando tests... ",
    l1 = linea_gram.parseString('A:b;')
    l2 = linea_gram.parseString('A:B;')
    l3 = linea_gram.parseString('A:*;')
    l4 = linea_gram.parseString('A:+;')
    l5 = linea_gram.parseString('A:+++++;')
    l6 = linea_gram.parseString('A:a|b;')
    l7 = linea_gram.parseString('A:|a;')
    l8 = linea_gram.parseString('A:|;')
    l9 = linea_gram.parseString('A:a|;')
    l10 = linea_gram.parseString('A:ab;')
    l11 = linea_gram.parseString('A:ab|c;')
    l12 = linea_gram.parseString('A:ab|c|d|;')
    l13 = linea_gram.parseString('D:abA||d++(e);')
    l14 = linea_gram.parseString('D:;')
    l15 = linea_gram.parseString('D:()|+;')
    print "OK!"
